package algebra.op;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import option.QueryKeywords;

import utils.Display;

import kb.DescriptionLogicsKB;
import entities.role.Role;

/**
 * This class implements a natural join (i.e., object id is *the* common
 * attribute) operator
 * 
 * @author j55wu
 * 
 */
public class JoinOP extends AlgebraicOP {
	private final AlgebraicOP q1, q2;
	private final DescriptionLogicsKB kb;

	public JoinOP(AlgebraicOP q1, AlgebraicOP q2) {
		this.q1 = q1;
		this.q2 = q2;
		this.kb = null;
	}

	// always retrieve left side op (i.e. q1) firt
	public List<AlgebraicOP> flattenedView() {
		List<AlgebraicOP> ret = new ArrayList<AlgebraicOP>();
		if (q1 instanceof JoinOP) {
			ret.addAll(((JoinOP) q1).flattenedView());
		} else {
			ret.add(q1);
		}
		if (q2 instanceof JoinOP) {
			ret.addAll(((JoinOP) q2).flattenedView());
		} else {
			ret.add(q2);
		}
		return ret;
	}

	public static AlgebraicOP generateIntersectionOP(List<ScanOP> ops) {
		if (ops.size() < 1) {
			return null;
		} else if (ops.size() == 1) {
			return ops.get(0);
		} else if (ops.size() == 2) {
			return new JoinOP(ops.get(0), ops.get(1));
		} else {
			JoinOP ip = new JoinOP(ops.get(0), ops.get(1));
			int i = 2;
			while (i < ops.size()) {
				ip = new JoinOP(ip, ops.get(i));
				i++;
			}
			return ip;
		}
	}

	public static AlgebraicOP generateIntersectionOP(Set<AlgebraicOP> ops) {
		if (ops.size() < 1) {
			return null;
		} else if (ops.size() == 1) {
			return ops.iterator().next();
		} else if (ops.size() == 2) {
			Iterator<AlgebraicOP> itp = ops.iterator();
			return new JoinOP(itp.next(), itp.next());
		} else {
			Iterator<AlgebraicOP> itp = ops.iterator();
			JoinOP ip = new JoinOP(itp.next(), itp.next());
			while (itp.hasNext()) {
				ip = new JoinOP(ip, itp.next());
			}
			return ip;
		}
	}

	public AlgebraicOP getFirstSubQuery() {
		return q1;
	}

	public AlgebraicOP getSecondSubQuery() {
		return q2;
	}

	@Override
	public boolean isPure() {
		if (q1.isPure() && q2.isPure()) {
			return true;
		}
		return false;
	}

	@Override
	public String toString() {
		return "(" + this.q1.toString() + " " + Display.QINTERUNICODE_NARY + " "
				+ this.q2.toString() + ")";
	}

	@Override
	public String toSerializedString() {
		return " (" + QueryKeywords.QINTER + " " + this.q1.toSerializedString()
				+ " " + this.q2.toSerializedString() + ") ";
	}

	@Override
	public DescriptionLogicsKB getPlanEvalKB() {
		return this.kb;
	}

	@Override
	public Set<Role> extractFeatures() {
		Set<Role> fs = new HashSet<Role>();
		fs.addAll(this.q1.extractFeatures());
		fs.addAll(this.q2.extractFeatures());
		return null;
	}

	@Override
	public Set<String> extractConstants() {
		Set<String> ks = new HashSet<String>();
		ks.addAll(this.q1.extractConstants());
		ks.addAll(this.q2.extractConstants());
		return ks;
	}

	@Override
	public boolean isLeaf() {
		return false;
	}

	@Override
	public AlgebraicOP getKFreeVersion() {
		return this;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((q1 == null) ? 0 : q1.hashCode());
		result = prime * result + ((q2 == null) ? 0 : q2.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (!(obj instanceof JoinOP))
			return false;
		JoinOP other = (JoinOP) obj;
		if (q1 == null) {
			if (other.q1 != null)
				return false;
		} else if (!q1.equals(other.q1))
			return false;
		if (q2 == null) {
			if (other.q2 != null)
				return false;
		} else if (!q2.equals(other.q2))
			return false;
		return true;
	}

}
